package com.cloud.common.exception;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.cloud.common.bean.ResultInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.List;

@Slf4j
@ResponseBody
@ControllerAdvice
@ConditionalOnMissingClass("com.cloud.gateway.bean.GatewayInfo")
public class GlobalExceptionHandler {
    /**
     * 内部错误
     *
     * @param request
     * @param exception
     * @return ResultInfo
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(Exception.class)
    public ResultInfo handleException(HttpServletRequest request, Exception exception) {
        if (exception instanceof UndeclaredThrowableException) {
            UndeclaredThrowableException ex = (UndeclaredThrowableException) exception;
            Throwable throwable = ex.getUndeclaredThrowable();
            if (throwable instanceof BlockException) {
                log.warn("requestURI={} execute break", request.getRequestURI());
                return new ResultInfo().setCode(HttpStatus.LOCKED.value());
            }
        }
        log.error("requestURI=" + request.getRequestURI() + " " + exception.getMessage(), exception);
        return new ResultInfo().setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
    }

    /**
     * 业务逻辑处理发生的异常
     *
     * @param exception
     * @return ResultInfo
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(AppException.class)
    public ResultInfo handleAppException(AppException exception) {
        if (StringUtils.isNotEmpty(exception.getDetail())) {
            log.error(exception.getDetail());
        }
        ResultInfo resultInfo = new ResultInfo().setCode(exception.getErrorCode());
        if (StringUtils.isNotEmpty(exception.getMessage())) {
            resultInfo.setMessage(exception.getMessage());
        }
        return resultInfo;
    }

    /**
     * 参数绑定错误
     *
     * @param exception
     * @return ResultInfo
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(BindException.class)
    public ResultInfo handleBindException(BindException exception) {
        String message = getParameterErrorMsg(exception.getBindingResult().getFieldErrors());
        return new ResultInfo().setCode(HttpStatus.BAD_REQUEST.value()).setMessage(message);
    }

    /**
     * 参数匹配错误
     *
     * @param exception
     * @return ResultInfo
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResultInfo handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException exception) {
        return new ResultInfo().setCode(HttpStatus.BAD_REQUEST.value());
    }

    /**
     * 参数验证错误
     *
     * @param exception
     * @return ResultInfo
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultInfo handleMethodArgumentNotValidException(MethodArgumentNotValidException exception) {
        String message = getParameterErrorMsg(exception.getBindingResult().getFieldErrors());
        return new ResultInfo().setCode(HttpStatus.BAD_REQUEST.value()).setMessage(message);
    }

    /**
     * 请求无效
     *
     * @param exception
     * @return ResultInfo
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResultInfo handleNotReadableException(HttpMessageNotReadableException exception) {
        return new ResultInfo().setCode(HttpStatus.BAD_REQUEST.value());
    }

    /**
     * 请求方法不支持
     *
     * @return ResultInfo
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResultInfo handleRequestMethodNotSupportedException() {
        return new ResultInfo().setCode(HttpStatus.METHOD_NOT_ALLOWED.value());
    }

    /**
     * 请求资源不存在
     *
     * @return ResultInfo
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(NoHandlerFoundException.class)
    public ResultInfo handleNoHandlerFoundException() {
        return new ResultInfo().setCode(HttpStatus.NOT_FOUND.value());
    }

    /**
     * 参数错误信息
     *
     * @param errorList
     * @return String
     */
    private String getParameterErrorMsg(List<FieldError> errorList) {
        if (CollectionUtils.isEmpty(errorList)) {
            return "参数错误";
        }
        StringBuilder message = new StringBuilder();
        for (FieldError fieldError : errorList) {
            message.append(fieldError.getDefaultMessage()).append(",");
        }
        message.deleteCharAt(message.length() - 1);
        return message.toString();
    }
}